using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Runtime.Serialization;
using System.Reflection;

namespace Rogue
{   
    [Serializable]
    public class GameMap : ISerializable
    {
        #region Public Member Variables
        public static readonly string PlayerName = "HERO";

        public MapCell[,] m_MapData;

        public GameCamera Camera
            { get { return m_GameCamera; } }

        public int Width
            { get { return m_MapData.GetLength(0); } }

        public int Height
            { get { return m_MapData.GetLength(1); } }

        public LightCaster LightCaster
            { get { return m_LightCaster; } }

        // is this the right place for this?
        public int CurrentDepth;

        public bool m_AllVisible;
        #endregion

        #region Private Member Variables
        private GameCamera m_GameCamera;
        private LightCaster m_LightCaster;
        //IEnumerator<LightCaster.RayData> m_LightCastStepper;
        //bool m_LIsDownLastFrame = false;
        private KeyboardState m_LastKeyState;
        // private Entity m_CurrentPlayableEntity;
        public Dictionary<string, Entity> m_EntityTable;

        public Vector3 m_ShadowColor;

        #endregion

        #region Constructors
        public GameMap()
        {
            m_GameCamera = new GameCamera(0, 0);
            // m_MapData = new MapCell[width, height];  // set by the map generator. Can't be accessed until then...
            m_LightCaster = new LightCaster(this);
            m_EntityTable = new Dictionary<string, Entity>();
            CurrentDepth = 0;
            m_AllVisible = false;   // for cheatz

            Human hero = new Human(PlayerName);
            hero.IsPlayerControlled = true;
            m_EntityTable.Add(PlayerName, hero);
            Timeline.Instance.Add(hero);
            Entity.Collector.Add(hero);

            m_ShadowColor = new Vector3(0.1f, 0.1f, 0.2f);

            m_GameCamera.SetTrackTarget(hero);

            m_LastKeyState = Keyboard.GetState();
            RegisterConsoleCommands();

        }
        #endregion

        #region Update
        public void Update(GameTime a_Time)
        {
            Timeline.Instance.Update(a_Time);

            m_GameCamera.Update(a_Time);

            
            Point pHoverCellCoord = m_GameCamera.MapMouseHoverCell;
            if (IsInBounds(pHoverCellCoord) && m_MapData[pHoverCellCoord.X, pHoverCellCoord.Y].Mapped)
            {
                MapCell MouseHoverCell = m_MapData[m_GameCamera.MapMouseHoverCell.X, m_GameCamera.MapMouseHoverCell.Y];
                string sPassableStatus = "(IMPASSABLE)";
                if (!TerrainDatabase.Instance.GetDefinition(MouseHoverCell.TerrainDefinition).BlocksActor)
                    sPassableStatus = "(PASSABLE)";      
                string sStatusMessage = TerrainDatabase.Instance.GetDefinition(MouseHoverCell.TerrainDefinition).LongDescription + sPassableStatus;
                string sTerrainMessage = string.Format("{0} {1}",
                    TerrainDatabase.Instance.GetDefinition(MouseHoverCell.TerrainDefinition).LongDescription,
                    sPassableStatus
                    );

                string sEntityMessage = "";
                foreach (Entity e in Entity.Collector.GetAllAt<Entity>(pHoverCellCoord.X, pHoverCellCoord.Y))
                {
                    sEntityMessage += string.Format(" : {0} ({1})", e.Name, e.GetType().ToString());
                }


                string sStatusString = sTerrainMessage + sEntityMessage;

                RogueGame.AddStatusString(sStatusString, 0, Color.Gray, CellWindow.Alignment.Left);
            }

            string sTimeMessage = "  [" + Timeline.GetTimestamp(Timeline.Instance.CurrentTime) + "]";
            // Update the status bar coordinates
            string sMouseCoords = "(" + pHoverCellCoord.X.ToString() + ", " + pHoverCellCoord.Y.ToString() + ")";
            RogueGame.AddStatusString(sMouseCoords + sTimeMessage, 158, Color.DarkGray, CellWindow.Alignment.Right);

            if (TutWindow.Instance.HasUnreadTuts())
            {
                RogueGame.AddStatusString("new (T)utorial", 140, Color.Yellow, CellWindow.Alignment.Right);
            }

            KeyboardState KBState = Keyboard.GetState();

            m_LightCaster.Reset();
            m_LightCaster.Origin = GetHero().Position;
            m_LightCaster.Radius = 15.0f;       // temp - depend on lightsources or something?

            m_LightCaster.castRays();

            foreach (KeyValuePair<Point, LightCaster.RayData> result in m_LightCaster.results)
            {
                LightCaster.RayData rd = result.Value;
                if (rd.m_IsVisible)
                {
                    m_MapData[result.Key.X, result.Key.Y].Mapped = true;
                }
            }

           
            
        }
        #endregion

        #region Helper Functions
        public void ClearEntities()
        {
            m_EntityTable.Clear();
            Entity.Collector.Clear();
        }

        public Entity GetEntity(string s_Name)
        {
            return m_EntityTable[s_Name];
        }

        public bool EntityExists(string s_Name)
        {
            return m_EntityTable.ContainsKey(s_Name);
        }

        // replace this with a map or something
        public Entity GetEntityAt(int a_x, int a_y)
        {
            foreach (Entity e in Entity.Collector.GetAllAt<Entity>(a_x, a_y))
                return e;

            return null;
        }

        public Entity GetEntityAt(Point a_pCell)
        {
            return GetEntityAt(a_pCell.X, a_pCell.Y);
        }

        // cache this instead
        public Entity GetHero()
        {
            foreach (KeyValuePair<Point, List<Entity>> p in Entity.Collector.Entities)
                foreach (Entity e in p.Value)
                    if (e.IsPlayerControlled)
                        return e;

            return null;
        }

        public void RemoveAllEntitiesButPlayer()
        {
            Entity hero = null;
            if (GetHero() != null)
                hero = GetEntity(PlayerName);
            else
            {
                hero = new Human(PlayerName);
                hero.IsPlayerControlled = true;
            }

            LabelHelper.Instance.RemoveAll();
            m_EntityTable.Clear();
            Entity.Collector.Clear();
            Timeline.Instance.Reset();
            Timeline.Instance.Add(hero);
            hero.NextThink = 0;
            m_EntityTable.Add(PlayerName, hero);
            Entity.Collector.Add(hero);
        }

        public void SpawnEntity<T>(string a_sName) where T : Entity
        {
            Entity e = (Entity)Activator.CreateInstance(typeof(T), new object[] { a_sName });

            Entity.Collector.Add(e);
            Timeline.Instance.Add(e);
        }

        public string SpawnEntity<T>() where T : Entity
        {
                Entity e = (Entity)Activator.CreateInstance(typeof(T));
                Entity.Collector.Add(e);    
                Timeline.Instance.Add(e);
                return e.Name;            
        }

        public void SpawnEntity(Type a_EntityType, string a_sName)
        { 
            if (a_EntityType.IsSubclassOf(typeof(Entity)))
            {
                Entity e = (Entity)Activator.CreateInstance(a_EntityType, new object[] {a_sName});
                Entity.Collector.Add(e);
                Timeline.Instance.Add(e);
            }
        }

        public string SpawnEntity(Type a_EntityType)
        {
            if (a_EntityType.IsSubclassOf(typeof(Entity)))
            {
                Entity e = (Entity)Activator.CreateInstance(a_EntityType);
                Entity.Collector.Add(e);
                Timeline.Instance.Add(e);
                return e.Name;
            }
            return null;
        }

        public void SpawnEntity(string a_sTypeName, string a_sName)
        {
            Type EntType = Type.GetType(a_sTypeName);
            SpawnEntity(EntType, a_sName);
        }

        public string SpawnEntity(string a_sTypeName)
        {
            Type EntType = Type.GetType("Rogue." + a_sTypeName);
            return SpawnEntity(EntType);
        }

        public Rectangle Bounds()
        {
            return new Rectangle(0, 0, Width, Height);
        }

        public bool IsInBounds(int a_X, int a_Y)
        {
            if (a_X >= 0 && a_Y >= 0 && a_X < Width && a_Y < Height)
                return true;
            else
                return false;
        }

        public bool IsInBounds(Point a_pTest)
        {
            if ((a_pTest.X >= 0) && (a_pTest.Y >= 0) &&
                (a_pTest.X < Width) && (a_pTest.Y < Height))
                return true;
            else
                return false;

        }

        public void SetAllTerrain(string a_sTerrainDef)
        {
            for (int y = 0; y < Height; y++)
            {
                for (int x = 0; x < Width; x++)
                {
                    SetTerrain(x, y, a_sTerrainDef);
                }
            }
        }

        public void SetTerrain(int a_X, int a_Y, string a_sTerrainDef)
        {
            m_MapData[a_X, a_Y].TerrainDefinition = a_sTerrainDef;
            
            TerrainDef def = TerrainDatabase.Instance.GetDefinition(a_sTerrainDef);

            m_MapData[a_X, a_Y].TerrainUpTile = (char)def.UpTile;
            m_MapData[a_X, a_Y].TerrainUpColor = new Color(def.UpColor);

            m_MapData[a_X, a_Y].TerrainForegroundTile = (char)def.ForegroundTile;
            m_MapData[a_X, a_Y].TerrainForeColor = new Color(def.ForeColor);

            m_MapData[a_X, a_Y].TerrainBackgroundTile = (char)def.BackgroundTile;
            m_MapData[a_X, a_Y].TerrainBackColor = new Color(def.BackColor);

            m_MapData[a_X, a_Y].BlocksLight = def.BlocksLight;
            m_MapData[a_X, a_Y].BlocksActor = def.BlocksActor;
            m_MapData[a_X, a_Y].Bloody = false;
        }


        public void BloodyTerrain(int a_X, int a_Y, bool a_bBloody)
        {
            m_MapData[a_X, a_Y].Bloody = true;
        }

        public void SetAllMapped(bool a_bMapped)
        {
            for (int y = 0; y < Height; y++)
            {
                for (int x = 0; x < Width; x++)
                {
                    m_MapData[x, y].Mapped = a_bMapped;
                }
            }
        }
        public Point MapCellToScreenCoordinates(int a_X, int a_Y, CellSubLocation a_SubLoc)
        {
            return TerrainWindow.Instance.CellSubLocationToScreenSpace(m_GameCamera.CellToWindowSpace(new Point(a_X, a_Y)), a_SubLoc);
        }


        public Point MapCellToScreenCoordinates(Point a_pMapCell, CellSubLocation a_SubLoc)
        {
            return TerrainWindow.Instance.CellSubLocationToScreenSpace(m_GameCamera.CellToWindowSpace(a_pMapCell), a_SubLoc);
        }

        public Rectangle GetMapCellScreenBounds(Point a_pMapCell)
        {
            return TerrainWindow.Instance.GetScreenCellBounds(m_GameCamera.CellToWindowSpace(a_pMapCell));
        }

        public bool BlocksLight(int a_X, int a_Y)
        {
            if (m_MapData[a_X, a_Y].BlocksLight)
                return true;

            foreach (Entity e in Entity.Collector.GetAllAt<Entity>(a_X, a_Y))
                if (e.BlocksLight())
                    return true;

            return false;
        }

        public bool BlocksActor(int a_X, int a_Y)
        {
            if (a_X < Width && a_X >= 0 && a_Y < Height && a_Y >= 0)
                return m_MapData[a_X, a_Y].BlocksActor;
            else
                return true;
        }

        public void DrawLine(Point a_pStart, CellSubLocation a_StartSubLoc, Point a_pDest, CellSubLocation a_DestSubLoc, Color a_Color)
        {
            TerrainWindow.Instance.DrawLine(m_GameCamera.CellToWindowSpace(a_pStart), a_StartSubLoc, m_GameCamera.CellToWindowSpace(a_pDest), a_DestSubLoc, a_Color);
        }

        public void DrawLine(int a_nStartX, int a_nStartY, CellSubLocation a_StartSubLoc, int a_nDestX, int a_nDestY, CellSubLocation a_DestSubLoc, Color a_Color)
        {
            TerrainWindow.Instance.DrawLine(
                m_GameCamera.CellToWindowSpace(new Point(a_nStartX, a_nStartY)), 
                a_StartSubLoc, 
                m_GameCamera.CellToWindowSpace(new Point(a_nDestX, a_nDestY)), 
                a_DestSubLoc, 
                a_Color
                );
        }


        public Point MapCellToScreenPos(int a_X, int a_Y, CellSubLocation a_SubLoc)
        {
            Point pScreenPos = m_GameCamera.CellToWindowSpace(new Point(a_X, a_Y));
            pScreenPos = TerrainWindow.Instance.CellSubLocationToScreenSpace(pScreenPos, a_SubLoc);

            return pScreenPos;
        }

        public Point MapCellToScreenPos(Point a_pCellCoord, CellSubLocation a_SubLoc)
        {
            Point pScreenPos = m_GameCamera.CellToWindowSpace(a_pCellCoord);
            pScreenPos = TerrainWindow.Instance.CellSubLocationToScreenSpace(pScreenPos, a_SubLoc);

            return pScreenPos;
        }

        public void DrawOutline(int a_nCellX, int a_nCellY, Color a_Color)
        {
            TerrainWindow.Instance.DrawCellOutline(m_GameCamera.CellToWindowSpace(new Point(a_nCellX, a_nCellY)), a_Color);
        }

        public void DrawOutline(Point a_pCell, Color a_Color)
        {
            TerrainWindow.Instance.DrawCellOutline(m_GameCamera.CellToWindowSpace(a_pCell), a_Color);
        }

        public void DrawConnection(int a_nStartCellX, int a_nStartCellY, int a_nDestCellX, int a_nDestCellY)
        {
            DrawConnection(a_nStartCellX, a_nStartCellY, a_nDestCellX, a_nDestCellY, Color.Gold, Color.Gold);
        }

        public void DrawConnection(int a_nStartCellX, int a_nStartCellY, int a_nDestCellX, int a_nDestCellY, Color a_LineColor, Color a_OutlineColor)
        {
            DrawLine(a_nStartCellX, a_nStartCellY, CellSubLocation.Center, a_nDestCellX, a_nDestCellY, CellSubLocation.Center, a_LineColor);
            DrawOutline(a_nStartCellX, a_nStartCellY, a_OutlineColor);
            DrawOutline(a_nDestCellX, a_nDestCellY, a_OutlineColor);
        }

        public void DrawConnection(Point a_pStartCell, Point a_pDestCell, Color a_LineColor, Color a_OutlineColor)
        {
            DrawLine(a_pStartCell, CellSubLocation.Center, a_pDestCell, CellSubLocation.Center, a_LineColor);
            DrawOutline(a_pStartCell, a_OutlineColor);
            DrawOutline(a_pDestCell, a_OutlineColor);
        }
        #endregion

        #region Rendering
        public void Draw(TerrainWindow a_TerrainWindow)
        {
            Rectangle rTerrainWindowRenderBounds = m_GameCamera.GetTerrainWindowRenderableArea(this, false);
            Rectangle rMapDataRenderBounds = m_GameCamera.GetMapRenderableArea(this, false);

            DrawInBounds(this, rMapDataRenderBounds, a_TerrainWindow, rTerrainWindowRenderBounds);
        }

        public float GetFracDistance(GameMap a_Map, int a_WorldX, int a_WorldY)
        {
            if (a_Map.m_AllVisible)
            {
                return 0.0f;
            }

            if (!a_Map.m_MapData[a_WorldX, a_WorldY].Mapped)
            {
                return 1.0f;
            }

            LightCaster.RayData ray = a_Map.LightCaster.GetRay(new Point(a_WorldX, a_WorldY));

            float fracDistance = 1.0f;      // a value 0 to 1 of how far we are to being invisible
            if (ray != null && ray.m_IsVisible)
            {
                // subtract one from the distance, otherwise nothing but the player would be fully lit
                float dist = (float)Math.Sqrt(ray.xLoc * ray.xLoc + ray.yLoc * ray.yLoc);
                fracDistance = Math.Max(0, dist - 1.0f) / a_Map.LightCaster.Radius;

                // bring the dist value closer to shadowed if we're carrying a shadow that isn't ours
                if (ray.m_IsCarryingShadow && 
                    !a_Map.BlocksActor(a_WorldX, a_WorldY) &&
                    !a_Map.BlocksLight(a_WorldX, a_WorldY))
                {
                    fracDistance = Math.Min(1.0f, fracDistance + 0.2f);
                }
            }

            return fracDistance;
        }

        public Color AdjustColorForLight(GameMap a_Map, int a_WorldX, int a_WorldY, float a_FracDistance, Color a_Color)
        {
            if (a_Map.m_AllVisible)
            {
                return a_Color;
            }
            else if (!a_Map.m_MapData[a_WorldX, a_WorldY].Mapped)
            {
                a_Color = Color.Black;
            }
            else
            {
                float shadowWeight = a_FracDistance;
                float colorWeight = 1.0f - a_FracDistance;
                a_Color = new Color(shadowWeight * m_ShadowColor + colorWeight * a_Color.ToVector3());
            }

            return a_Color;
        }

        Entity bestEntityOnTile(int worldX, int worldY)
        {
            // Are there any entities to render?
            List<Entity> entitiesOnTile = null;
            if (!Entity.Collector.Entities.TryGetValue(new Point(worldX, worldY), out entitiesOnTile))
                return null;

            // TODO: DF style flipping to show overlapping things
            // TODO: better priorities for drawing things over each other. Current priority: alive > static > everything else
            Entity entityToRender = entitiesOnTile.Find(delegate(Entity e) { return e.IsAlive; });
            if (entityToRender == null)
                entityToRender = entitiesOnTile.Find(delegate(Entity e) { return e.Speed != -1; });
            if (entityToRender == null)
                entityToRender = entitiesOnTile[0];

            return entityToRender;
        }

        Color GetBGColorWithBlood(GameMap a_Map, int worldX, int worldY)
        {
            if (a_Map.m_MapData[worldX, worldY].Bloody)
            {
                uint bigPrime = 16777619;
                uint salt = 2166136261;
                long hashX = (worldX * bigPrime + salt);
                long hashY = (worldY * bigPrime + salt);
                return new Color((byte)(Color.Maroon.R + (hashX + hashY) % 55),
                                 (byte)(Color.Maroon.G + (hashX + hashY) % 50),
                                 (byte)(Color.Maroon.B + (hashX + hashY) % 45));
            }
            else
            {
                return a_Map.m_MapData[worldX, worldY].TerrainBackColor;
            }
        }

        public void DrawInBounds(GameMap a_Map, Rectangle a_MapArea, TerrainWindow a_TerrainWindow, Rectangle a_TerrainWindowArea)
        {
            Entity hero = GetHero();

            for (int y = 0; y < a_MapArea.Height; y++)
            {
                for (int x = 0; x < a_MapArea.Width; x++)
                {
                    int worldX = a_MapArea.Left + x;
                    int worldY = a_MapArea.Top + y;

                    if (!a_Map.IsInBounds(worldX, worldY))
                        continue;

                    float fracDist = GetFracDistance(a_Map, worldX, worldY);

                    Color upColor = Color.TransparentBlack;
                    if (worldY < a_Map.Height - 1)
                        upColor = AdjustColorForLight(a_Map, worldX, worldY + 1, fracDist, a_Map.m_MapData[worldX, worldY + 1].TerrainUpColor);

                    if (upColor.A > 0 && !BlocksActor(worldX, worldY) && hero != null)
                    {
                        float distSq = (hero.X - worldX) * (hero.X - worldX) + (hero.Y - worldY) * (hero.Y - worldY);
                        if (distSq < 16.0f)
                        {
                            upColor.A = (byte) (16 * distSq);
                        }
                    }

                    Color backColor = AdjustColorForLight(a_Map, worldX, worldY, fracDist, GetBGColorWithBlood(a_Map, worldX, worldY));

                    char upTile = '\0';
                    if (worldY < a_Map.Height - 1)
                        upTile = a_Map.m_MapData[worldX, worldY + 1].TerrainUpTile;

                    a_TerrainWindow.PutChar(
                        a_Map.m_MapData[worldX, worldY].TerrainForegroundTile, backColor,
                        a_Map.m_MapData[worldX, worldY].TerrainBackgroundTile, backColor,
                        upTile, upColor,
                        a_TerrainWindowArea.X + x, a_TerrainWindowArea.Y + y,
                        0, 0,
                        fracDist);

                    Entity entityToRender = bestEntityOnTile(worldX, worldY);
                    if (entityToRender != null)
                    {
                        bool bCriticallyWounded = entityToRender.IsCriticallyWounded();

                        // Is this tile visible? if it's not, we don't need to render any entities that move.
                        LightCaster.RayData ray = a_Map.LightCaster.GetRay(new Point(worldX, worldY));
                        if (!a_Map.m_AllVisible && (ray == null || !ray.m_IsVisible) && entityToRender.Speed != -1 && entityToRender.IsAlive)
                        {
                            continue;
                        }

                        EntityDef entDef = EntityDatabase.Instance.GetDefinition(entityToRender.GetType().Name);
                        Color EntColor = new Color(entDef.DisplayColor);
                        if (!entityToRender.IsAlive)
                            EntColor = Color.Maroon;

                        if (bCriticallyWounded && entityToRender.IsAlive)
                        {
                            double nSinCheck = Math.Sin(Timeline.Instance.LastGameTime.TotalRealTime.TotalSeconds * 20);
                            if (nSinCheck < 0)
                            {
                                a_TerrainWindow.PutChar('+', a_TerrainWindowArea.X + x, a_TerrainWindowArea.Y + y, Color.Red.ToVector4(), Color.White.ToVector4());
                                continue;
                            }
                        }

                        EntColor = AdjustColorForLight(a_Map, entityToRender.X, entityToRender.Y, fracDist, EntColor);

                        // This is kind of hacky. We need a better way to split out player-specific things?
                        char toDisplay = (char) entDef.DisplayCharacter;
                        if (entityToRender.IsPlayerControlled)
                            toDisplay = (char) EntityDatabase.Instance.GetDefinition("Hero").DisplayCharacter;

                        a_TerrainWindow.PutForegroundChar(toDisplay, EntColor, a_TerrainWindowArea.X + x, a_TerrainWindowArea.Y + y);
                    }

                    Entity tallEntity = null;
                    if (worldY < a_Map.Height - 1)
                        tallEntity = bestEntityOnTile(worldX, worldY + 1);

                    if (tallEntity != null)
                    {
                        EntityDef entDef = EntityDatabase.Instance.GetDefinition(tallEntity.GetType().Name);
                        if (entDef.UpCharacter == '\0')
                            continue;

                        Color EntColor = new Color(entDef.DisplayColor);
                        if (!tallEntity.IsAlive)
                            EntColor = Color.Maroon;

                        EntColor = AdjustColorForLight(a_Map, tallEntity.X, tallEntity.Y, fracDist, EntColor);

                        // Is this tile visible? if it's not, we don't need to render any entities that move.
                        LightCaster.RayData ray = a_Map.LightCaster.GetRay(new Point(worldX, worldY+1));
                        if (!a_Map.m_AllVisible && (ray == null || !ray.m_IsVisible) && tallEntity.Speed != -1)
                        {
                            continue;
                        }

                        a_TerrainWindow.PutForegroundChar((char)entDef.UpCharacter, EntColor, a_TerrainWindowArea.X + x, a_TerrainWindowArea.Y + y);
                    }
                }
            }
        }

        public void ExampleDrawDelegate(GameMap a_Map, Rectangle a_MapArea, TerrainWindow a_TerrainWindow, Rectangle a_TerrainWindowArea)
        {
            for (int y = 0; y < a_MapArea.Height; y++)
            {
                for (int x = 0; x < a_MapArea.Width; x++)
                {
                    if (Random.Instance.Next(2) > 0)
                        a_TerrainWindow.SetForegroundColor(a_TerrainWindowArea.X + x, a_TerrainWindowArea.Y + y, Color.Red);
                    else
                        a_TerrainWindow.SetForegroundColor(a_TerrainWindowArea.X + x, a_TerrainWindowArea.Y + y, Color.Blue);
                }
            }

            a_Map.DrawConnection(
                Random.Instance.Next(0, a_Map.Width),
                Random.Instance.Next(0, a_Map.Height),
                Random.Instance.Next(0, a_Map.Width),
                Random.Instance.Next(0, a_Map.Height)
            );

            if (a_MapArea.Width > 0 && a_MapArea.Height > 0)
            {
                a_TerrainWindow.SetBackgroundColor(
                    Random.Instance.Next(a_TerrainWindowArea.X, a_TerrainWindowArea.Right),
                    Random.Instance.Next(a_TerrainWindowArea.Y, a_TerrainWindowArea.Bottom),
                    Color.White
                    );
            }
        }
        #endregion
   
        #region Console Commands

        private void RegisterConsoleCommands()
        {       
            ConsoleWindow.Instance.RegisterFunction("MAP.NEW", CONSOLE_CreateMap);
            ConsoleWindow.Instance.RegisterFunction("MAP.CREATE", CONSOLE_CreateMap);
            ConsoleWindow.Instance.RegisterFunction("MAP.RESET", CONSOLE_CreateMap);
            
            ConsoleWindow.Instance.RegisterFunction("MAP.PLOT", CONSOLE_PlotTerrain);
            ConsoleWindow.Instance.RegisterFunction("MAP.PLOTRANGE", CONSOLE_PlotTerrainRange);
            ConsoleWindow.Instance.RegisterFunction("MAP.PLOTALL", CONSOLE_SetAllTerrain);
            ConsoleWindow.Instance.RegisterFunction("MAP.RANDOMIZE", CONSOLE_RandomizeTerrain);
            ConsoleWindow.Instance.RegisterFunction("MAP.SETALL.MAPPED", CONSOLE_SetAllMapped);

            ConsoleWindow.Instance.RegisterFunction("CAMERA.SETPOS", CONSOLE_CameraSetPos);
            ConsoleWindow.Instance.RegisterFunction("CAMERA.GLIDETO", CONSOLE_CameraGlideTo);
            ConsoleWindow.Instance.RegisterFunction("CAMERA.RESET", CONSOLE_CameraReset);
            ConsoleWindow.Instance.RegisterFunction("CAMERA.ZOOM.TOGGLE", CONSOLE_CameraToggleZoom);
            ConsoleWindow.Instance.RegisterFunction("CAMERA.ZOOM.IN", CONSOLE_CameraZoomIn);
            ConsoleWindow.Instance.RegisterFunction("CAMERA.ZOOM.OUT", CONSOLE_CameraZoomOut);
            ConsoleWindow.Instance.RegisterFunction("CAMERA.TRACK", CONSOLE_CameraTrackEntity);
            ConsoleWindow.Instance.RegisterFunction("CAMERA.STOPTRACKING", CONSOLE_CameraStopTracking);

            ConsoleWindow.Instance.RegisterFunction("ENTITY.SETPOS", CONSOLE_EntitySetPos);
            ConsoleWindow.Instance.RegisterFunction("ENTITY.SPAWN", CONSOLE_SpawnEntity);
            ConsoleWindow.Instance.RegisterFunction("ENTITY.SPAWN.ATRANDOM", CONSOLE_SpawnEntityAtRandom);
            ConsoleWindow.Instance.RegisterFunction("ENTITY.POSSESS", CONSOLE_EntityPossess);
        }
        private void CONSOLE_CreateMap(string[] a_sArgs)
        {
            if (a_sArgs == null)
            {
                m_MapData = null;
                m_MapData = new MapCell[100, 100];
                SetAllTerrain("terrain.grass");
                RemoveAllEntitiesButPlayer();
                return;
            }

            int nSizeX = System.Convert.ToInt32(a_sArgs[0]);
            int nSizeY = System.Convert.ToInt32(a_sArgs[1]);

            m_MapData = null;
            m_MapData = new MapCell[nSizeX, nSizeY];

            RemoveAllEntitiesButPlayer();
            
            if (a_sArgs.Length > 2)
            {
                if (TerrainDatabase.Instance.ContainsDefinition(a_sArgs[2]))
                    SetAllTerrain(a_sArgs[2]);
                else
                    SetAllTerrain("terrain.grass");
            }
            else
            {
                SetAllTerrain("terrain.grass");
            }

        }

        private void CONSOLE_CameraSetPos(string[] a_sArgs)
        {
            int nSizeX = System.Convert.ToInt32(a_sArgs[0]);
            int nSizeY = System.Convert.ToInt32(a_sArgs[1]);
            m_GameCamera.SetPosition(new Point(nSizeX, nSizeY));
        }

        private void CONSOLE_CameraGlideTo(string[] a_sArgs)
        {
            int nPosX = System.Convert.ToInt32(a_sArgs[0]);
            int nPosY = System.Convert.ToInt32(a_sArgs[1]);
            m_GameCamera.GlideTo(nPosX, nPosY);
        }

        private void CONSOLE_CameraToggleZoom(string[] a_sArgs)
        {
            m_GameCamera.ToggleZoomLevel();
            return;   
        }

        private void CONSOLE_CameraZoomIn(string[] a_sArgs)
        {
            m_GameCamera.SetZoomLevel(GameCamera.Zoom.Normal);
        }

        private void CONSOLE_CameraZoomOut(string[] a_sArgs)
        {
            m_GameCamera.SetZoomLevel(GameCamera.Zoom.Wide);
        }

        private void CONSOLE_CameraReset(string[] a_sArgs)
        {
            m_GameCamera.SetPosition(new Point(0, 0));
        }

        private void CONSOLE_CameraTrackEntity(string[] a_sArgs)
        {
            if (a_sArgs.Length > 0)
            {
                if (m_EntityTable.ContainsKey(a_sArgs[0]))
                    m_GameCamera.SetTrackTarget(m_EntityTable[a_sArgs[0]]);
            }
            else
            {
                m_GameCamera.ClearTrackTarget();
            }
        }

        private void CONSOLE_CameraStopTracking(string[] a_sArgs)
        {
            m_GameCamera.ClearTrackTarget();
        }

        private void CONSOLE_SetAllTerrain(string[] a_sArgs)
        {
            if (TerrainDatabase.Instance.ContainsDefinition(a_sArgs[0]))
                SetAllTerrain(a_sArgs[0]);
            else
            {
                ConsoleWindow.Instance.PrintError("INVALID TERRAIN: " + a_sArgs[0]);
            }

        }

       private void CONSOLE_PlotTerrain(string[] a_sArgs)
       {
            int nPosX = System.Convert.ToInt32(a_sArgs[0]);
            int nPosY = System.Convert.ToInt32(a_sArgs[1]);
            if (TerrainDatabase.Instance.ContainsDefinition(a_sArgs[2]))
            {
                if (IsInBounds(new Point(nPosX, nPosY)))
                    SetTerrain(nPosX, nPosY, a_sArgs[2]);
            }
            else
            {
                ConsoleWindow.Instance.PrintError("INVALID TERRAIN: " + a_sArgs[2]);
            }

        }

        private void CONSOLE_PlotTerrainRange(string[] a_sArgs)
        { 
            int nStartPosX = System.Convert.ToInt32(a_sArgs[0]);
            int nStartPosY = System.Convert.ToInt32(a_sArgs[1]);
            int nDestPosX = System.Convert.ToInt32(a_sArgs[2]);
            int nDestPosY = System.Convert.ToInt32(a_sArgs[3]);

            if (TerrainDatabase.Instance.ContainsDefinition(a_sArgs[4]))
            {
                for (int y = nStartPosY; y <= nDestPosY; y++)
                {
                    for (int x = nStartPosX; x <= nDestPosX; x++)
                    {
                        if (IsInBounds(new Point(x, y)))
                            SetTerrain(x, y, a_sArgs[4]);
                    }
                }
            }
            else
            {
                ConsoleWindow.Instance.PrintError("INVALID TERRAIN: " + a_sArgs[4]);
            }           
        }

        private void CONSOLE_RandomizeTerrain(string[] a_sArgs)
        {
            List<string> ValidTerrains = new List<string>();
            foreach (string sTerrain in a_sArgs)
            {
                if (TerrainDatabase.Instance.ContainsDefinition(sTerrain))
                    ValidTerrains.Add(sTerrain);
            }

            if (ValidTerrains.Count < 1)
            {
                ConsoleWindow.Instance.PrintError("INVALID TERRAIN TYPES: ");
                return;
            }

            for (int y = 0; y < Height; y++)
            {
                for (int x = 0; x < Width; x++)
                {
                    string sTerrain = ValidTerrains[Random.Instance.Next(0, ValidTerrains.Count)];
                    SetTerrain(x, y, sTerrain);
                    
                }
            }

            SetAllMapped(false);
        }

        private void CONSOLE_SetAllMapped(string[] a_sArgs)
        {
            bool bMapAll = System.Convert.ToBoolean(a_sArgs[0]);
            SetAllMapped(bMapAll);
        }

        private void CONSOLE_EntitySetPos(string[] a_sArgs)
        {
            if (m_EntityTable.ContainsKey(a_sArgs[0]))
            {
                int nPosX = System.Convert.ToInt32(a_sArgs[1]);
                int nPosY = System.Convert.ToInt32(a_sArgs[2]);
                m_EntityTable[a_sArgs[0]].X = nPosX;
                m_EntityTable[a_sArgs[0]].Y = nPosY;
            }
        }

        private void CONSOLE_SpawnEntity(string[] a_sArgs)
        {
            string sName; 
            try
            {
                sName = SpawnEntity(a_sArgs[0]);
                ConsoleWindow.Instance.PrintError("\"" + sName + "\" has been spawned.");
            }
            catch (System.Exception e)
            {
                throw new System.Exception("Exception spawning Entity of type " + "\"" + a_sArgs[0] + "\" : " + e.Message);
            }
            if (a_sArgs.Length > 2)
            {
                int nPosX = System.Convert.ToInt32(a_sArgs[1]);
                int nPosY = System.Convert.ToInt32(a_sArgs[2]);
                GetEntity(sName).MoveTo(new Point(nPosX, nPosY));
            }
        }

        private void CONSOLE_SpawnEntityAtRandom(string[] a_sArgs)
        {
            string sName;
            try
            {
                sName = SpawnEntity(a_sArgs[0]);
                ConsoleWindow.Instance.PrintError("\"" + sName + "\" has been spawned.");
            }
            catch (System.Exception e)
            {
                throw new System.Exception("Exception spawning Entity of type " + "\"" + a_sArgs[0] + "\" : " + e.Message);
            }

            bool bBlocksActor = true;
            while (bBlocksActor)
            {
                int nRandX = Random.Instance.Next(Width);
                int nRandY = Random.Instance.Next(Height);
                if (!BlocksActor(nRandX, nRandY))
                {
                    GetEntity(sName).Position = new Point(nRandX, nRandY);
                    bBlocksActor = false;
                }
               
            }

        }

        private void CONSOLE_EntityPossess(string[] a_sArgs)
        {
            Entity e = GetEntity(a_sArgs[0]);
            bool bShouldControl = true;
            try
            {
                bShouldControl = System.Convert.ToBoolean(a_sArgs[1]);
            }
            catch
            {

            }

            e.IsPlayerControlled = bShouldControl;
        }
        #endregion

        #region Serialization
        public GameMap(SerializationInfo info, StreamingContext context)
        {
            m_GameCamera = new GameCamera(0, 0);
            m_GameCamera.SetPosition((Point)info.GetValue("m_GameCamera.Position", typeof(Point)));
            m_MapData = (MapCell[,])info.GetValue("m_MapData", typeof(MapCell[,]));
            m_LightCaster = new LightCaster(this);
           
            // Clear old entities
            m_EntityTable = new Dictionary<string, Entity>();
            m_EntityTable = (Dictionary<string, Entity>)info.GetValue("m_EntityTable", typeof(Dictionary<string, Entity>));

            // make the collector update based on what we just loaded
            Entity.Collector.Repopulate(this);

            m_AllVisible = false;

            m_LastKeyState = Keyboard.GetState();
            RegisterConsoleCommands();
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("m_MapData", m_MapData);
            info.AddValue("m_GameCamera.Position", m_GameCamera.GetPosition());
            info.AddValue("m_EntityTable", m_EntityTable);
        }
        #endregion
    }
       
    
}








